package com.wsc.indexer;

 

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.util.List;
import java.util.Locale;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.MethodNotSupportedException;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.nio.DefaultHttpServerIODispatch;
import org.apache.http.impl.nio.DefaultNHttpServerConnection;
import org.apache.http.impl.nio.DefaultNHttpServerConnectionFactory;
import org.apache.http.impl.nio.SSLNHttpServerConnectionFactory;
import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
import org.apache.http.nio.NHttpConnectionFactory;
import org.apache.http.nio.NHttpServerConnection;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.nio.protocol.BasicAsyncRequestConsumer;
import org.apache.http.nio.protocol.BasicAsyncResponseProducer;
import org.apache.http.nio.protocol.HttpAsyncExchange;
import org.apache.http.nio.protocol.HttpAsyncRequestConsumer;
import org.apache.http.nio.protocol.HttpAsyncRequestHandler;
import org.apache.http.nio.protocol.HttpAsyncRequestHandlerRegistry;
import org.apache.http.nio.protocol.HttpAsyncService;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.ListeningIOReactor;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.params.SyncBasicHttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.ImmutableHttpProcessor;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import com.wsc.Config;
import com.wsc.crawler.init.CrawlerConfig;
import com.wsc.indexer.utils.HtmlData;

import static com.wsc.crawler.grabber.Grabber.isStopped;

/*
 * This is Implementation of Asynchronous HTTP Server, which can handle multiple connections in parallel.
 * 
 */

public class IndexerHTTPServer {

	public static Logger log=Logger.getLogger(IndexerHTTPServer.class.getName());

	// This static fields are used in ServeClient class.
	
	private static IndexerConfig config=new IndexerConfig() ;
	
	public static LuceneIndexer indexer_2xx;
	public static LuceneIndexer indexer_3xx;
	public static LuceneIndexer indexer_4xx;
	public static LuceneIndexer indexer_Other;
	
	// get Instance of ServeClientRequest class and use this instance for all requests made
	// by crawler.
	
	private static ServeClientRequest request_hanler=new ServeClientRequest();
	
	
	public IndexerHTTPServer(IndexerConfig config){
		IndexerHTTPServer.config = config;
		
		// Instantiate the indexers
		indexer_2xx = new LuceneIndexer(config.getIndex_2xx_dir());
		indexer_3xx = new LuceneIndexer(config.getIndex_3xx_dir());
		indexer_4xx = new LuceneIndexer(config.getIndex_4xx_dir());
		indexer_Other = new LuceneIndexer(config.getIndex_other_dir());
	}
	
	
	public static void main(String...as){
		
		try {
			config.loadDefaultXmlFile();
			(new IndexerHTTPServer(config)).start();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
 
	
    public void start() throws Exception { 
 
    	log.debug("Staring Indexer Server...");
        /*
         * Request Listen Port for Server.
         * Put this port in Serverconfig.xml file.
         * While intializing server get it from config file, and make this as listen port.
         * 
         *  And put secondary port field also, because if another server listening on same port, then 
         *  port binding exception will arise. To avoid this the intializer will intialize the server 
         *  with secodary listen port.
         */
    	
        int port =config.getIndexserver().getPort();

        // HTTP parameters for the server
        HttpParams params = new SyncBasicHttpParams();
        params
            .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
            .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
            .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpTest/1.1");
     
        // Create HTTP protocol processing chain
       
        HttpProcessor httpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[] {
                // Use standard server-side protocol interceptors
                new ResponseDate(),
                new ResponseServer(),
                new ResponseContent(),
                new ResponseConnControl()
        });
        // Create request handler registry
        HttpAsyncRequestHandlerRegistry reqistry = new HttpAsyncRequestHandlerRegistry();
        // Register the default handler for all URIs
        reqistry.register("*", new HttpRequestHandler());
        // Create server-side HTTP protocol handler
        HttpAsyncService protocolHandler = new HttpAsyncService(
                httpproc, new DefaultConnectionReuseStrategy(), reqistry, params) {

            @Override
            public void connected(final NHttpServerConnection conn) {
                System.out.println(conn + ": connection open");
                super.connected(conn);
            }

            @Override
            public void closed(final NHttpServerConnection conn) {
                System.out.println(conn + ": connection closed");
                super.closed(conn);
            }

        };
        
        // Create HTTP connection factory
        NHttpConnectionFactory<DefaultNHttpServerConnection> connFactory;
        if (port == 8443) {
            // Initialize SSL context
            ClassLoader cl = IndexerHTTPServer.class.getClassLoader();
            URL url = cl.getResource("my.keystore");
            if (url == null) {
                System.out.println("Keystore not found");
                System.exit(1);
            }
            KeyStore keystore  = KeyStore.getInstance("jks");
            keystore.load(url.openStream(), "secret".toCharArray());
            KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
                    KeyManagerFactory.getDefaultAlgorithm());
            kmfactory.init(keystore, "secret".toCharArray());
            KeyManager[] keymanagers = kmfactory.getKeyManagers();
            SSLContext sslcontext = SSLContext.getInstance("TLS");
            sslcontext.init(keymanagers, null, null);
            connFactory = new SSLNHttpServerConnectionFactory(sslcontext, null, params);
        } else {
            connFactory = new DefaultNHttpServerConnectionFactory(params);
        }
        // Create server-side I/O event dispatch
        IOEventDispatch ioEventDispatch = new DefaultHttpServerIODispatch(protocolHandler, connFactory);
        // Create server-side I/O reactor
        ListeningIOReactor ioReactor = new DefaultListeningIOReactor();
        try {
            // Listen of the given port
            ioReactor.listen(new InetSocketAddress(port));
            // Ready to go!
            ioReactor.execute(ioEventDispatch);
        } catch (InterruptedIOException ex) {
            System.err.println("Interrupted");
        } catch (IOException e) {
            System.err.println("I/O error: " + e.getMessage());
        }
        System.out.println("Shutdown");
    }

    static class HttpRequestHandler implements HttpAsyncRequestHandler<HttpRequest> {


        public HttpRequestHandler() {
            super();
        }

        public HttpAsyncRequestConsumer<HttpRequest> processRequest(
                final HttpRequest request,
                final HttpContext context) {
            // Buffer request content in memory for simplicity
            return new BasicAsyncRequestConsumer();
        }

        public void handle(
                final HttpRequest request,
                final HttpAsyncExchange httpexchange,
                final HttpContext context) throws HttpException, IOException {
            HttpResponse response = httpexchange.getResponse();
            handleInternal(request, response, context);
            httpexchange.submitResponse(new BasicAsyncResponseProducer(response));
        }

        private void handleInternal(
                final HttpRequest request,
                final HttpResponse response,
                final HttpContext context) throws HttpException, IOException {

            String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
            if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) {
                throw new MethodNotSupportedException(method + " method not supported");
            }

            
            		// If Client post any data in its body part then this block will
         			// execute.
         			if (request instanceof HttpEntityEnclosingRequest) {
 
          				// get entity from request.
         				HttpEntity entity = ((HttpEntityEnclosingRequest) request)
         						.getEntity();

         				// Convert it into byteArray.
         				String xml = EntityUtils.toString(entity);

         				System.out.println("Incoming entity content (bytes): "
         						+ xml.length());
         	
         				// writing xml to a file
         	     	 		
         				File f=new File("postedURLS.xml");
         				FileOutputStream st=new FileOutputStream(f);
         				DataOutputStream d=new DataOutputStream(st);
         				d.writeBytes(xml);
         				d.close();
         				st.close();
         			 
         			}

         			// Get the index of the character in requested path in HttpRequest.
         			int index = request.getRequestLine().getUri().indexOf('?');

         			// If index is greater than 0 means that requested path has atleat a
         			// key-value pair.
         			if (index > 0) {
         				
         			//	give the request over to the following method,
         			// which checks and handles the request based on the parameters
         				log.debug("Request forwording to ServeClientRequest class");
         				request_hanler.handleRequest(request, response, context,config);
         			}else{
         				response.setStatusCode(HttpStatus.SC_OK);
						
						// Construct a String Entity, with proper message.
						NStringEntity entity = null;
						
						// Get HTMl from HtmlData class
						String HTML=HtmlData.getWelcomeHTML();
						
						//set Entity body.
						entity = new NStringEntity(HTML);
						
						response.setHeader("Content-Type", "text/html");
						
						response.setEntity(entity);	
         			}

               }  

    }
}

